/*
 * Copyright (C) 2021 ETH Zurich, University of Bologna
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Title: io_mux.h
 * Author: Manuel Eggimann  <meggimann@iis.ee.ethz.ch>
 * Date: 25.08.2021
 *
 * Description: Control the pad configuration and multiplexing of the Siracusa chip
 */

#ifndef IO_MUX_H
#define IO_MUX_H

#ifndef LANGUAGE_ASSEMBLY

#include <stdint.h>
#include "pulpissimo_padframe_all_pads_regs.h"

#define IO_MUX_PAD_CFG_REG_OFFSET 0x4
#define IO_MUX_PAD_MUX_SEL_REG_OFFSET 0x8
#define IO_MUX_PAD_REG_SEPARATION 0x8


typedef enum {
  IO_MUX_NO_PULL, ///< No pull resistor is enabled
  IO_MUX_PULL_EN, ///< Enable internal pull-up/down (depends on pad kind) resistor
} io_mux_pull_cfg_e;

/**
 * Pad configuration struct
 *
 * Keep in mind, that certain peripherals might take over control of certain pad
 * config signals (e.g. rx_en) if connected to a pad. The settings you provide
 * through this API only take effect unless not overriden by the currently
 * connected peripheral.
 */
typedef struct {
  io_mux_pull_cfg_e pull_cfg; ///< Pull-up/down settings of the pad
  uint8_t tx_en; ///< Tx enable
  uint8_t rx_en; ///< Rx enable
} io_mux_cfg_t;

/**
 * Pad modes
 *
 * This enum defines all the different roles a particular IO pad can assume.
 * Keep in mind, that except for the GPIO role you must not assign the same role
 * to the more than one pad.
 */
typedef enum {
  PAD_MODE_DISABLED = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_REGISTER,
  PAD_MODE_CPI0_DATA0 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_CPI0_DATA0,
  PAD_MODE_CPI0_DATA1 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_CPI0_DATA1,
  PAD_MODE_CPI0_DATA2 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_CPI0_DATA2,
  PAD_MODE_CPI0_DATA3 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_CPI0_DATA3,
  PAD_MODE_CPI0_DATA4 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_CPI0_DATA4,
  PAD_MODE_CPI0_DATA5 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_CPI0_DATA5,
  PAD_MODE_CPI0_DATA6 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_CPI0_DATA6,
  PAD_MODE_CPI0_DATA7 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_CPI0_DATA7,
  PAD_MODE_CPI0_DATA8 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_CPI0_DATA8,
  PAD_MODE_CPI0_DATA9 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_CPI0_DATA9,
  PAD_MODE_CPI0_HSYNC = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_CPI0_HSYNC,
  PAD_MODE_CPI0_PCLK = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_CPI0_PCLK,
  PAD_MODE_CPI0_VSYNC = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_CPI0_VSYNC,
  PAD_MODE_GPIO = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_GPIO_GPIO00,
  PAD_MODE_I2C0_SCL = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_I2C0_SCL,
  PAD_MODE_I2C0_SDA = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_I2C0_SDA,
  PAD_MODE_I2S0_MASTER_SCK = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_I2S0_MASTER_SCK,
  PAD_MODE_I2S0_MASTER_SD0 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_I2S0_MASTER_SD0,
  PAD_MODE_I2S0_MASTER_SD1 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_I2S0_MASTER_SD1,
  PAD_MODE_I2S0_MASTER_WS = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_I2S0_MASTER_WS,
  PAD_MODE_I2S0_SLAVE_SCK = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_I2S0_SLAVE_SCK,
  PAD_MODE_I2S0_SLAVE_SD0 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_I2S0_SLAVE_SD0,
  PAD_MODE_I2S0_SLAVE_SD1 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_I2S0_SLAVE_SD1,
  PAD_MODE_I2S0_SLAVE_WS = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_I2S0_SLAVE_WS,
  PAD_MODE_QSPIM0_CSN0 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_QSPIM0_CSN0,
  PAD_MODE_QSPIM0_CSN1 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_QSPIM0_CSN1,
  PAD_MODE_QSPIM0_CSN2 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_QSPIM0_CSN2,
  PAD_MODE_QSPIM0_CSN3 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_QSPIM0_CSN3,
  PAD_MODE_QSPIM0_SCK = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_QSPIM0_SCK,
  PAD_MODE_QSPIM0_SDIO0 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_QSPIM0_SDIO0,
  PAD_MODE_QSPIM0_SDIO1 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_QSPIM0_SDIO1,
  PAD_MODE_QSPIM0_SDIO2 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_QSPIM0_SDIO2,
  PAD_MODE_QSPIM0_SDIO3 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_QSPIM0_SDIO3,
  PAD_MODE_SDIO0_SDCLK = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_SDIO0_SDCLK,
  PAD_MODE_SDIO0_SDCMD = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_SDIO0_SDCMD,
  PAD_MODE_SDIO0_SDDATA0 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_SDIO0_SDDATA0,
  PAD_MODE_SDIO0_SDDATA1 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_SDIO0_SDDATA1,
  PAD_MODE_SDIO0_SDDATA2 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_SDIO0_SDDATA2,
  PAD_MODE_SDIO0_SDDATA3 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_SDIO0_SDDATA3,
  PAD_MODE_TIMER0_OUT0 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_TIMER0_OUT0,
  PAD_MODE_TIMER0_OUT1 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_TIMER0_OUT1,
  PAD_MODE_TIMER0_OUT2 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_TIMER0_OUT2,
  PAD_MODE_TIMER0_OUT3 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_TIMER0_OUT3,
  PAD_MODE_TIMER1_OUT0 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_TIMER1_OUT0,
  PAD_MODE_TIMER1_OUT1 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_TIMER1_OUT1,
  PAD_MODE_TIMER1_OUT2 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_TIMER1_OUT2,
  PAD_MODE_TIMER1_OUT3 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_TIMER1_OUT3,
  PAD_MODE_TIMER2_OUT0 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_TIMER2_OUT0,
  PAD_MODE_TIMER2_OUT1 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_TIMER2_OUT1,
  PAD_MODE_TIMER2_OUT2 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_TIMER2_OUT2,
  PAD_MODE_TIMER2_OUT3 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_TIMER2_OUT3,
  PAD_MODE_TIMER3_OUT0 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_TIMER3_OUT0,
  PAD_MODE_TIMER3_OUT1 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_TIMER3_OUT1,
  PAD_MODE_TIMER3_OUT2 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_TIMER3_OUT2,
  PAD_MODE_TIMER3_OUT3 = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_TIMER3_OUT3,
  PAD_MODE_UART0_RX = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_UART0_RX,
  PAD_MODE_UART0_TX = PULPISSIMO_PADFRAME_ALL_PADS_CONFIG_PAD_IO00_MUX_SEL_PAD_IO00_MUX_SEL_VALUE_PORT_UART0_TX
} io_mux_mode_e;

typedef enum {
  PAD_GPIO00,
  PAD_GPIO01,
  PAD_GPIO02,
  PAD_GPIO03,
  PAD_GPIO04,
  PAD_GPIO05,
  PAD_GPIO06,
  PAD_GPIO07,
  PAD_GPIO08,
  PAD_GPIO09,
  PAD_GPIO10,
  PAD_GPIO11,
  PAD_GPIO12,
  PAD_GPIO13,
  PAD_GPIO14,
  PAD_GPIO15,
  PAD_GPIO16,
  PAD_GPIO17,
  PAD_GPIO18,
  PAD_GPIO19,
  PAD_GPIO20,
  PAD_GPIO21,
  PAD_GPIO22,
  PAD_GPIO23,
  PAD_GPIO24,
  PAD_GPIO25,
  PAD_GPIO26,
  PAD_GPIO27,
  PAD_GPIO28,
  PAD_GPIO29,
  PAD_GPIO30,
  PAD_GPIO31,
  NUM_PADS
} io_mux_pad_e;

/**
 * Change the pad configuration of the given pad
 *
 * @param pad The pad to configure
 * @param cfg [in] The configuration settings for the pad
 */
void io_mux_config_set(io_mux_pad_e pad, io_mux_cfg_t const *cfg);

/**
 * Reads the current pad configuration from the pad control peripheral
 *
 * @param pad The pad from which to read the current configuration
 * @param cfg [out] Pointer to the pad config structure where the obtained config will be written to
 */
void io_mux_config_get(io_mux_pad_e pad, io_mux_cfg_t *cfg);

/**
 * Change the pad mode (IO multiplex settings)
 *
 * This function reconfigures the pad multiplexer to route the given IO signal
 * to the pad. You must only route the same IO signal to one pad. E.g. you must
 * not put PAD_GPIO04 in mode PAD_MODE_I3C0_SCL while PAD_GPIO01 is already in
 * the same mode. The only exception to this rule is the PAD_MODE_GPIO. You can
 * put all pads into this mode simultaneously to have access to up to NUM_PADS
 * GPIOs.
 *
 * @param pad The pad for which the pad multiplex setting should be changed
 */
void io_mux_mode_set(io_mux_pad_e pad, io_mux_mode_e mode);

/**
 * Obtain the current mode of a pad
 *
 * @param pad The pad for which to return the current pad mode
 * @returns The current mode of the pad
 */
io_mux_mode_e io_mux_mode_get(io_mux_pad_e pad);

#endif
#endif /* IO_MUX_H */
